#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <signal.h>
#include <pthread.h>

#include "../include/icam.h"

SOCKET serv_skt;

struct sockaddr_in server_addr;
struct sockaddr_in icam_addr;
INT32U server_addr_len = sizeof(server_addr);


INT32S socket_bind(SOCKET skt, struct sockaddr_in *addr)
{
    INT32S ret;

    ret = bind(skt, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
    if (ret != 0) {
        printf("bind error\n");
    }

    return ret;
}

INT32S socket_connect(SOCKET skt, struct sockaddr_in *addr)
{
    INT32S ret;

    ret = connect(skt, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
    if (ret != 0) {
        printf("connect error\n");
    }

    return ret;
}

INT32S icam_cmd_parser(t_icam_cmd *cmd)
{
    INT32S ret = -1;

    if (strncmp(cmd->magic, ICAM_CMD_MAGIC, 4) != 0) {
        debug_printf("icam_cmd magic error: %c%c%c%c\n", cmd->magic[0], cmd->magic[1], cmd->magic[2], cmd->magic[3]);
        return -1;
    }

    debug_printf("id = %d\n", cmd->cmd_id);
    switch (cmd->cmd_id) {
        case ICAM_CMD_ICAM_NOT_LOGON :
            ret = -2;
            break;
        case ICAM_CMD_ICAM_LOG_INFO :
            icam_addr.sin_family = AF_INET;
            icam_addr.sin_port = VIEWER_NET_REGISTRY_PORT;
            icam_addr.sin_addr.s_addr = *(INT32U *)(cmd->buf);
            ret = 0;
            break;
        default :
            break;
    }

    return ret;
}

void viewer_registry_connect_establish(void)
{
    struct sockaddr_in local_addr;

    struct hostent *host = NULL;

    serv_skt = socket(AF_INET, SOCK_DGRAM, 0);

    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(VIEWER_NET_SOURCE_PORT);
    local_addr.sin_addr.s_addr = htons(INADDR_ANY);
    
    if (socket_bind(serv_skt, &local_addr) != 0) {
        ///< TODO
        while (1) ;
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(VIEWER_NET_REGISTRY_PORT);
    if ((server_addr.sin_addr.s_addr = inet_addr(VIEWER_NET_REGISTRY_NAME)) == INADDR_NONE) {
        host = gethostbyname(ICAM_NET_REGISTRY_NAME);
        if (host == NULL) {
            memcpy(&(server_addr.sin_addr), host->h_addr_list[0], host->h_length);
        } else {
            debug_printf("gethostbyname() faild\n");
            ///< TODO
            while (1) ;
        }
    }
}

int viewer_find_icam(INT32U icam_id)
{
    t_icam_cmd icam_cmd;
    INT32S ret;

    memcpy(icam_cmd.magic, ICAM_CMD_MAGIC, 4);
    icam_cmd.cmd_id = ICAM_CMD_FIND_ICAM;
    icam_cmd.payload_length = 4;
    *(INT32U *)icam_cmd.buf = icam_id;
    
    ///< send the requeset to Registry server
    ///< TODO timeout and request retransmition may needed
    sendto(serv_skt, &icam_cmd, icam_cmd.payload_length + ICAM_MAX_CMD_HEAD_SIZE, 0, (struct sockaddr *) &server_addr, sizeof(server_addr));

    while (1) {
        ret = recvfrom(serv_skt, &icam_cmd, ICAM_MAX_CMD_SIZE, 0, (struct sockaddr *) &server_addr, &server_addr_len);
        if (ret == -1) {
            debug_printf("recv cmd error\n");
        } else {
            ret = icam_cmd_parser(&icam_cmd);
            if ((ret == 0) || (ret == -2)) {
                break;
            }
        }
    }

    return ret;
}

///< TODO this maybe implemented in a indenpendent thread
void viewer_connect_to_icam(void)
{
    t_icam_cmd icam_cmd;
    INT32S ret;

    memcpy(icam_cmd.magic, ICAM_CMD_MAGIC, 4);
    icam_cmd.cmd_id = ICAM_CMD_FIND_ICAM;
    icam_cmd.payload_length = 0;

    //sendto(serv_skt, &icam_cmd, icam_cmd.payload_length + ICAM_MAX_CMD_HEAD_SIZE, 0, (struct sockaddr *) &icam_addr, sizeof(icam_addr));
    while (1) {
        ret = recvfrom(serv_skt, &icam_cmd, ICAM_MAX_CMD_SIZE, 0, (struct sockaddr *) &server_addr, &server_addr_len);
        if (ret == -1) {
            debug_printf("v_c_i: recv cmd error\n");
        } else {
            debug_printf("v_c_i: recv cmd: %d\n", icam_cmd.cmd_id);
        }
    }

}


int main(int argc, char *argv[])
{
    INT8U cmd_id_buf[32];
    INT32U icam_id;

    viewer_registry_connect_establish();

    while (1) {
        debug_printf("Please input iCam id:\n");
        gets(cmd_id_buf);
        icam_id = atoi(cmd_id_buf);
        if (viewer_find_icam(icam_id) == 0) {
            debug_printf("iCam[%d] from: %s:%d\n",
                    icam_id,
                    inet_ntoa(icam_addr.sin_addr), 
                    ntohs(icam_addr.sin_port));
            viewer_connect_to_icam();
        } else {
            debug_printf("iCam[%d] not log on\n", icam_id); 
        }
    }
    return 0;
}
